package dabs.engine;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import dabs.engine.control.GameCommand;
import dabs.engine.control.OperatorSoftware;
import dabs.engine.environment.Environment;
import dabs.engine.messaging.Notification;
import dabs.engine.messaging.NotificationHandler;
import dabs.engine.messaging.PostOffice;
import dabs.engine.plant.PowerPlant;


public class GameEngine implements Runnable {
	private boolean stopped = false, 
			          paused = false;
	
	private double tickSpeed = 1; // measured in ticks per second
	private int ticksPassed = 0;      
	
	private Environment environment;
	private PostOffice poffice;
	private PowerPlant powerPlant;
	private OperatorSoftware operatorSoftware;
	
	// this will normally be the interface, it can of course be a dummy interface also
	private NotificationHandler gameInterface; 

	// public API
	public PostOffice  getPostOffice()  { return poffice; }
	public Environment getEnvironment() { return environment; }
	public PowerPlant  getPowerPlant()  { return powerPlant; }
	public OperatorSoftware getOperatorSoftware() { return operatorSoftware; }

	public int getValue()       { return ticksPassed; }
	public boolean isPaused()  { return paused; }
	public boolean isStopped() { return stopped; }
	
	// default constructor, cause in java "you can't optional arguments"
	public GameEngine() { 
		this(1);
	}
	
	public GameEngine(int ticksPerSecond) {
		this.tickSpeed = ticksPerSecond;
		poffice = new PostOffice();
		powerPlant = new PowerPlant(poffice);
		environment = new Environment(poffice, powerPlant);
		operatorSoftware = new OperatorSoftware(poffice, powerPlant);
	}

	public void run() {
		while(!stopped) {
			try {
				Thread.sleep((int)(1000 / tickSpeed)); // remember, tickSpeed = ticks / second
			} catch (InterruptedException e) {
				System.out.println("Interrupted engine... bad");
			}
			if (!paused) tick();
		}
	}
	
	public void registerInterface(NotificationHandler gameInterface) {
		this.gameInterface = gameInterface;
		poffice.registerHandler(gameInterface);
	}
	
	protected synchronized void tick() {
		ticksPassed++;
		environment.tick();
		powerPlant.tick();
	}
	
	private void save() {
		try {
			FileOutputStream fileOut = new FileOutputStream("test.sav");
			ObjectOutputStream out = new ObjectOutputStream(fileOut);
			out.writeInt(ticksPassed);
			out.writeObject(poffice);
			out.writeObject(powerPlant);
			out.writeObject(environment);
			out.writeObject(operatorSoftware);
			out.close();
			fileOut.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void load() {
		try { 
			FileInputStream fileIn = new FileInputStream("test.sav");
			ObjectInputStream in = new ObjectInputStream(fileIn);
			ticksPassed = in.readInt();
			poffice  = (PostOffice) in.readObject();
			powerPlant = (PowerPlant) in.readObject();
			environment = (Environment) in.readObject();
			operatorSoftware = (OperatorSoftware) in.readObject();
			in.close();
			fileIn.close();
			
			poffice.registerHandler(gameInterface); // re-register the interface
		} catch (FileNotFoundException e) {
			poffice.handleNotification(new Notification("", "Load file not found."));
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	private void stop() { stopped = true; }
	
	public synchronized void  runCommand(GameCommand command) {
		String cmd = command.getCommand();
		if       (cmd.equals("quit") || cmd.equals("exit")) {  stop(); }
		else if (cmd.equals("save")) {  save(); }
		else if (cmd.equals("load")) { load();  }
		else if (cmd.equals("pause") || cmd.equals("resume")) {
			
			// if we try to 'pause' when we're paused or 'resume' when we're running
			if (paused == cmd.equals("pause")) {
				poffice.handleNotification(new Notification("", "Erm, you're already " + (paused ? "paused" : "running") + ", you know..."));
			} else {
				paused = !paused; // toggle
			}
		} else {
			throw new RuntimeException("Unexisting command!");
		}
	}
}

